using System;
using System.ComponentModel;
using System.DirectoryServices.Protocols;
using InfiniTec.Data;

namespace InfiniTec.DirectoryServices
{
	/// <summary>
	/// Since the System.DirectoryServices.Protocols classes only return 
	/// properties as byte[] or as string values, this class is used to preserve
	/// the original type, until a specific target type is requested.
	/// </summary>
	public class RawProperty : Property, IPrototypeProperty
	{
		private readonly DirectoryAttribute _Attribute;

		internal RawProperty(DirectoryAttribute attribute) : base(new PropertyDefinition<object>(attribute.Name, false))
		{
			_Attribute = attribute;
		}


		/// <summary>
		/// Gets the property as as typed property.
		/// </summary>
		/// <returns></returns>
		public Property<T> ParseProperty<T>()
		{
			var definition = new PropertyDefinition<T>(_Attribute.Name, false);

			if (_Attribute.Count == 0) return new Property<T>(definition);

			if (typeof (T) == typeof (byte[]))
			{
				var property = new Property<T>(definition, (T) _Attribute.GetValues(typeof (byte[]))[0]);
				property.ResetChanged();
				return property;
			}
			if (typeof (T) == typeof (Guid))
			{
				var id = new Guid((byte[]) _Attribute.GetValues(typeof (byte[]))[0]);
				var property = (Property<T>) (object) new Property<Guid>(new PropertyDefinition<Guid>(_Attribute.Name, false), id);
				property.ResetChanged();
				return property;
			}
			var value = (string) _Attribute.GetValues(typeof (string))[0];

			if (typeof (T) == typeof (string))
			{
				var property = new Property<T>(definition, (T) (object) value);
				property.ResetChanged();
				return property;
			}

			var result = new Property<T>(definition, (T) TypeDescriptor.GetConverter(typeof (T)).ConvertFromString(value));
			result.ResetChanged();
			return result;
		}

		/// <summary>
		/// Gets the property as multi valued property of the specified type.
		/// </summary>
		/// <returns></returns>
		public MultiValuedProperty<T> ParseMultiValuedProperty<T>()
		{
			var definition = new PropertyDefinition<T>(_Attribute.Name, true);

			if (_Attribute.Count == 0) return new MultiValuedProperty<T>(definition);

			if (typeof (T) == typeof (byte[]))
			{
				var property = new MultiValuedProperty<T>(definition, (T[]) (object) _Attribute.GetValues(typeof (byte[])));
				property.ResetChanged();
				return property;
			}
			TypeConverter converter = TypeDescriptor.GetConverter(typeof (T));
			var values = (string[]) _Attribute.GetValues(typeof (string));
			MultiValuedProperty<T> result;
			if (typeof (T) == typeof (string))
			{
				result = new MultiValuedProperty<T>(definition, (T[]) (object) values);
				result.ResetChanged();
				return result;
			}

			T[] typedValues = Array.ConvertAll(values, input => (T) converter.ConvertFromString(input));

			result = new MultiValuedProperty<T>(definition, typedValues);
			return result;
		}
	}
}